<?php

namespace app\index\dao;

use app\common\model\Trade;
use ShidianUtil\Concurrent\Synchronizer;
use ShidianUtil\Thinkphp\Dao\DaoTrait;
use ShidianUtil\Utils\TimeUtil;
use think\Collection;
use think\facade\Config;

/**
 * TradeDao 数据访问对象
 * Class TradeDao
 * @package app\dao
 * @method Trade findByPK(?int $id, $field = true)
 * @method \think\Collection selectAll(?int $page, ?int $page_size, $field = true)
 * @method Trade insertActive(array $params, array $allow_field = [])
 * @method Trade updateActive(Trade $model, array $params, array $allow_field = [])
 * @method Trade findAndUpdateActive(int $id, array $params, array $allow_field = [])
 * @method Trade updateActiveByClosure(\Closure $closure, array $params, array $allow_field = [])
 * @method void deleteModel(Trade $model)
 * @method void deleteById(int|array|\Closure $id, bool $force = false)
 * @link https://www.kancloud.cn/manual/thinkphp6_0/1037579
 * @link https://www.kancloud.cn/manual/think-orm/1257998
 * @link https://www.kancloud.cn/thinkphp/master-database-and-model/265551
 */
class TradeDao
{
    use DaoTrait;

    /**
     * @var \app\common\model\Trade
     */
    private $baseModel;

    /**
     * TradeDao constructor.
     */
    public function __construct()
    {
        $this->baseModel = Trade::class;
    }

    /**
     * @param $sourceId
     * @param $memberId
     * @return Trade|null
     */
    public function getTradeBySource($sourceId, $memberId): ?Trade
    {
        return Trade::where('sourceId', $sourceId)
            ->where('memberId', $memberId)
            ->where('tradeType', TRADE_SOURCE)
            ->where('payStatus', Trade::PAID)
            ->whereIn('refundStatus', [Trade::NOTREFUND, Trade::REFUNDING, Trade::REFUND_FAILED])
            ->field(true)
            ->find();
    }

    /**
     * 我的案源订单-详情
     * @param $id
     * @return Trade|null
     */
    public function findSourceTrade($id): ?Trade
    {
        return Trade::where('id', $id)
            ->where('tradeType', TRADE_SOURCE)
            ->where('payStatus', Trade::PAID)
            ->whereIn('refundStatus', [Trade::NOTREFUND, Trade::REFUNDING, Trade::REFUND_FAILED])
            ->field(true)
            ->find();
    }

    /**
     * 统计某线索的订单总计多少钱
     * @param $sourceId
     * @return float
     */
    public function sumSourceTradePayMoney($sourceId)
    {
        return Trade::where('sourceId', $sourceId)
            ->where('tradeType', TRADE_SOURCE)
            ->where('payStatus', Trade::PAID)
            ->whereIn('refundStatus', [Trade::NOTREFUND, Trade::REFUNDING, Trade::REFUND_FAILED])
            ->sum('payMoney');
    }

    /**
     * 我的案源订单-列表
     * @param $memberId
     * @param $sourceStatus
     * @param $sourceType
     * @param $page
     * @param $page_size
     * @return Collection
     */
    public function selectSourceTrade($memberId, $sourceStatus, $sourceType, $page, $page_size): Collection
    {

        return Trade::where('memberId', $memberId)
            ->where('tradeType', TRADE_SOURCE)
            ->where('payStatus', Trade::PAID)
            ->whereIn('refundStatus', [Trade::NOTREFUND, Trade::REFUNDING, Trade::REFUND_FAILED])
            ->where('attach->buyout', '0')
            ->when(not_blank($sourceStatus), fn($query) => $query->where('sourceStatus', $sourceStatus))
            ->when(not_blank($sourceType), fn($query) => $query->where('attach->source_type', $sourceType))
            ->field(true)
            ->order('id', 'DESC')
            ->paging($page, $page_size)
            ->select();
    }

    public function getTradeById($id): ?Trade
    {
        return $this->queryTrade()
            ->where('id', $id)
            ->find();
    }

    public function recentTrade($memberId): ?Trade
    {
        $time = time() - 60;

        return $this->queryTrade()
            ->where('memberId', $memberId)
            ->where('created', '>=', $time)
            ->order('id', 'DESC')
            ->find();
    }

    private function queryTrade()
    {
        return Trade::field(['id', 'memberId', 'orderNo', 'payStatus', 'payExpTime', 'num', 'showMoney', 'totalMoney',
            'payMoney', 'tradeType', 'landingType', 'consultingId', 'serverId', 'serverSkuId', 'payMethod', 'payMethodScene',
            'created'])
            ->withAttr([
                'showMoney' => fn($value) => (float)$value,
            ]);
    }

    /**
     * 插入订单
     * @param $params
     * @return Trade|null
     */
    public function insertTrade($params)
    {

        $record = null;

        $signature = __CLASS__;
        $this->getSynchronizer()->synchronized($signature, function () use ($params, &$record) {

            if (is_blank($params['orderNo'] ?? null)) {
                $params['orderNo'] = ($params['orderNoPre'] ?? '') . $this->getUid(4);
            }

            unset($params['orderNoPre']);

            $record = $this->insertActive($params);
        });

        if (!$record) {
            throw new \RuntimeException('下单失败，请稍后再试');
        }

        return $record;
    }

    /**
     * 我的订单
     * @param $memberId
     * @param $status
     * @param $page
     * @param $page_size
     * @return Collection
     */
    public function selectMyTrades($memberId, $status, $page, $page_size): Collection
    {
        $now = time();

        $query = Trade::where('memberId', $memberId);

        if ($status == 0) {
            // 待付款
            $query->where('payStatus', Trade::UNPAID)
                ->where('payExpTime', '>', time());
        } elseif ($status == 1) {
            // 进行中
            $query->where('payStatus', Trade::PAID)
                ->where('refundStatus', Trade::NOTREFUND)
                ->where(fn($query) => $query->whereOr('endTime', '>', time())->whereNull('endTime', 'OR'));
        } elseif ($status == 2) {
            // 待评价
            $query->where('payStatus', Trade::PAID)
                ->where('refundStatus', Trade::NOTREFUND)
                ->where('endTime', '<=', time())
                ->where('isComment', 0);
        } elseif ($status == 3) {
            // 退款
            $query->where('payStatus', Trade::PAID)
                ->whereIn('refundStatus', [Trade::REFUNDING, Trade::REFUNDED, Trade::REFUND_FAILED]);

            $query->orderValues('refundStatus', [Trade::REFUNDING, Trade::REFUNDED, Trade::REFUND_FAILED])
                ->order('refundRequestTime', 'DESC');
        } elseif ($status == 4) {
            // 已评价
            $query->where('payStatus', Trade::PAID)
                ->where('refundStatus', Trade::NOTREFUND)
                ->where('endTime', '<=', time())
                ->where('isComment', 1);
        } else {
            return \think\model\Collection::make();
        }

        $collection = $query->with([
            'consulting',
            'server',
        ])
            ->withAttr(['status' => fn() => $status,])
            ->append(['status'])
            ->hidden(['consulting', 'server'])
            ->order('created', 'DESC')
            ->paging($page, $page_size)
            ->select();

        $collection->each(function (Trade $trade) use ($now) {
            if ($trade->consulting && not_blank($trade->consulting->ask)) {
                $title = $trade->consulting->ask;
            } else {

                if ($trade->server) {
                    $title = $trade->server->name;
                } else {
                    $title = Config::get('trade.trades.' . $trade->tradeType . '.name', '');
                    if (is_blank($title)) {
                        $title = '订单';
                    }
                }

                if ($trade->status == '0') {
                    $title = '待支付' . $title;
                }
            }

            $trade->title = $title;

            // 订单退款
            if (at_night($now)) {

                $trade->refundRequestType = 3;
            } else {

                if ($trade->refundRequestFirstClickTime > 0 && $trade->refundRequestFirstClickTime < ($now - 15 * 60)) {

                    $trade->refundRequestType = 1;
                } else {

                    $trade->refundRequestType = 2;
                }
            }

        });

        return $collection;
    }

    /**
     * 某用户是否购买了某案源
     * @param $memberId
     * @param $sourceId
     * @return bool
     */
    public function isBuySource($memberId, $sourceId): bool
    {
        $record = Trade::where('memberId', $memberId)
            ->where('sourceId', $sourceId)
            ->where('payStatus', Trade::PAID)
            ->whereIn('refundStatus', [Trade::NOTREFUND, Trade::REFUND_FAILED])
            ->field(['id'])
            ->find();

        return !is_null($record);
    }

    private function getSynchronizer(): Synchronizer
    {
        return app(Synchronizer::class);
    }

    /**
     * 按毫秒数生成订单号
     * @param $pad_length
     * @return string
     */
    private function getUid($pad_length = 8)
    {
        $time = microtime(true);

        $z = date('z', $time);
        if (strlen($z) < 3) {
            $z = str_pad($z, 3, '0', STR_PAD_LEFT);
        }

        $second = $time - TimeUtil::stamp(TimeUtil::DAY, $time);
        $second = bcmul($second, 1000);
        $second = str_pad($second, $pad_length, '0', STR_PAD_LEFT);

        return date('y', $time) . $z . $second;
    }
}
